package com.yfp.client.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.NumberUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yfp.client.domain.*;
import com.yfp.client.domain.ClientHealthLog;
import com.yfp.client.domain.ClientHistoryInfo;
import com.yfp.client.domain.Combo;
import com.yfp.client.domain.OrderList;
import com.yfp.client.domain.dto.ClientHealthLogDTO;
import com.yfp.client.domain.vo.ClientHealthLogListVO;
import com.yfp.client.domain.vo.ClientHealthLogStatisticVO;
import com.yfp.client.domain.vo.ClientHealthLogVO;
import com.yfp.client.mapper.ClientHealthLogMapper;
import com.yfp.client.service.*;
import com.yfp.client.service.*;
import com.yfp.client.util.SecurityUtils;
import jakarta.annotation.Resource;
import org.springframework.stereotype.Service;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.time.LocalTime;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @description 针对表【client_health_log(客户健康餐饮记录)】的数据库操作Service实现
 * @createDate 2025-02-11 17:26:41
 */
@Service
public class ClientHealthLogServiceImpl extends ServiceImpl<ClientHealthLogMapper, ClientHealthLog> implements ClientHealthLogService {

    @Resource
    private ClientHealthLogMapper clientHealthLogMapper;
    @Resource
    private ClientHistoryInfoService clientHistoryInfoService;
    @Resource
    private OrderListService orderListService;
    @Resource
    private ComboService comboService;
    @Resource
    private ClientService clientService;

    @Override
    public ClientHealthLogStatisticVO getClientHealthLogStatistic(Date startTime, Date endTime) {
        String clientId = SecurityUtils.getClientId();

        List<ClientHealthLog> clientHealthLogs = clientHealthLogMapper.selectByClientIdAndDate(clientId, startTime, endTime).stream().filter(item -> item.getStatus() == 1).toList();

        // 计算脂肪、蛋白质、碳水化合物、热量总和，以及占比
        ClientHealthLogStatisticVO statisticVO = calculateNutritionStatistics(clientId, clientHealthLogs, startTime, endTime);
        return statisticVO;
    }

    private ClientHealthLogStatisticVO calculateNutritionStatistics(String clientId, List<ClientHealthLog> clientHealthLogs, Date startTime, Date endTime) {
        ClientHealthLogStatisticVO statisticVO = new ClientHealthLogStatisticVO();

        BigDecimal totalFat = BigDecimal.ZERO;
        BigDecimal totalProtein = BigDecimal.ZERO;
        BigDecimal totalCarbohydrate = BigDecimal.ZERO;
        BigDecimal totalKcal = BigDecimal.ZERO;

        for (ClientHealthLog log : clientHealthLogs) {
            if (log.getWeight() == null || log.getFat() == null || log.getProtein() == null || log.getCarbohydrate() == null) {
                continue; // 忽略无效数据
            }

            ClientHealthLogVO clientHealthLogVO = calculateNutritionValues(BeanUtil.toBean(log, ClientHealthLogVO.class));

            totalFat = totalFat.add(NumberUtil.toBigDecimal(clientHealthLogVO.getFat()));
            totalProtein = totalProtein.add(NumberUtil.toBigDecimal(clientHealthLogVO.getProtein()));
            totalCarbohydrate = totalCarbohydrate.add(NumberUtil.toBigDecimal(clientHealthLogVO.getCarbohydrate()));
            totalKcal = totalKcal.add(NumberUtil.toBigDecimal(clientHealthLogVO.getKcal()));
        }

        statisticVO.setFat(totalFat.setScale(1, RoundingMode.HALF_UP).toString());
        statisticVO.setProtein(totalProtein.setScale(1, RoundingMode.HALF_UP).toString());
        statisticVO.setCarbohydrate(totalCarbohydrate.setScale(1, RoundingMode.HALF_UP).toString());
        statisticVO.setIntakeKcal(totalKcal.setScale(1, RoundingMode.HALF_UP).intValue());

        // 计算占比
        BigDecimal totalFatAndProteinAndCarbohydrate = NumberUtil.add(statisticVO.getFat(), statisticVO.getProtein(), statisticVO.getCarbohydrate());
        if (NumberUtil.equals(totalFatAndProteinAndCarbohydrate, BigDecimal.ZERO)) {
            statisticVO.setFatPercentage(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).toString());
            statisticVO.setProteinPercentage(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).toString());
            statisticVO.setCarbohydratePercentage(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).toString());
        } else {
            statisticVO.setFatPercentage(NumberUtil.div(NumberUtil.toBigDecimal(statisticVO.getFat()), totalFatAndProteinAndCarbohydrate).multiply(NumberUtil.toBigDecimal("100")).setScale(1, RoundingMode.HALF_UP).toString());
            statisticVO.setProteinPercentage(NumberUtil.div(NumberUtil.toBigDecimal(statisticVO.getProtein()), totalFatAndProteinAndCarbohydrate).multiply(NumberUtil.toBigDecimal("100")).setScale(1, RoundingMode.HALF_UP).toString());
            statisticVO.setCarbohydratePercentage(NumberUtil.div(NumberUtil.toBigDecimal(statisticVO.getCarbohydrate()), totalFatAndProteinAndCarbohydrate).multiply(NumberUtil.toBigDecimal("100")).setScale(1, RoundingMode.HALF_UP).toString());
        }

        // 男性，BMR = 88.362 + (13.397 × 体重kg) + (4.799 × 身高cm) - (5.677 × 年龄)
        // 女性，BMR = 447.593 + (9.247 × 体重kg) + (3.098 × 身高cm) - (4.330 × 年龄)
        // 获取最近的一条记录
        ClientHistoryInfo clientHistoryInfo = clientHistoryInfoService.lastClientHealthLog(clientId, endTime);
        if (Objects.nonNull(clientHistoryInfo)) {
            // 计算总日常能量消耗（TDEE）
            calculateTdee(clientHistoryInfo, statisticVO, totalKcal);
        }else{
            ClientHistoryInfo clientHistoryInfo1 = clientHistoryInfoService.firstClientHealthLog(clientId, startTime);
            // 计算总日常能量消耗（TDEE）
            calculateTdee(clientHistoryInfo1, statisticVO, totalKcal);
        }

        return statisticVO;
    }

    private void calculateTdee(ClientHistoryInfo clientHistoryInfo, ClientHealthLogStatisticVO statisticVO, BigDecimal totalKcal) {
        int age = DateUtil.ageOfNow(clientHistoryInfo.getBirthday());
        if (clientHistoryInfo.getSex().equals("男")) {
            BigDecimal weight = NumberUtil.mul(NumberUtil.toBigDecimal("13.397"), NumberUtil.toBigDecimal(clientHistoryInfo.getWeight()));
            BigDecimal height = NumberUtil.mul(NumberUtil.toBigDecimal("4.799"), NumberUtil.toBigDecimal(clientHistoryInfo.getHeight()));
            BigDecimal age2 = NumberUtil.mul(NumberUtil.toBigDecimal("5.677"), NumberUtil.toBigDecimal(age));

            BigDecimal subtract = NumberUtil.add(NumberUtil.toBigDecimal("88.362"), weight, height).subtract(age2);
            statisticVO.setTdee(subtract.setScale(1, RoundingMode.HALF_UP).intValue());
            statisticVO.setGapKcal(NumberUtil.sub(statisticVO.getTdee(), totalKcal).setScale(1, RoundingMode.HALF_UP).intValue());
        } else if (clientHistoryInfo.getSex().equals("女")) {
            BigDecimal weight = NumberUtil.mul(NumberUtil.toBigDecimal("9.247"), NumberUtil.toBigDecimal(clientHistoryInfo.getWeight()));
            BigDecimal height = NumberUtil.mul(NumberUtil.toBigDecimal("3.098"), NumberUtil.toBigDecimal(clientHistoryInfo.getHeight()));
            BigDecimal age2 = NumberUtil.mul(NumberUtil.toBigDecimal("4.330"), NumberUtil.toBigDecimal(age));

            BigDecimal subtract = NumberUtil.add(NumberUtil.toBigDecimal("447.593"), weight, height).subtract(age2);
            statisticVO.setTdee(subtract.setScale(1, RoundingMode.HALF_UP).intValue());
            statisticVO.setGapKcal(NumberUtil.sub(statisticVO.getTdee(), totalKcal).setScale(1, RoundingMode.HALF_UP).intValue());
        }
    }


    @Override
    public List<ClientHealthLogListVO> getClientHealthLogList(Date startTime, Date endTime) {
        String clientId = SecurityUtils.getClientId();

        List<ClientHealthLog> clientHealthLogs = clientHealthLogMapper.selectByClientIdAndDate(clientId, startTime, endTime);
        List<String> orderListIds = clientHealthLogs.stream().map(ClientHealthLog::getOrderListId).toList();
        if (CollUtil.isEmpty(orderListIds)) {
            return null;
        }
        List<OrderList> orderLists = orderListService.listByIds(orderListIds);
        List<String> comboIds = orderLists.stream().map(OrderList::getComboId).toList();
        List<Combo> combos = comboService.listByPackageIds(comboIds);


        // 将 ClientHealthLog 转换为 ClientHealthLogVO 并映射图片信息
        List<ClientHealthLogVO> clientHealthLogVOs = clientHealthLogs.stream().map(clientHealthLog -> {
            ClientHealthLogVO bean = BeanUtil.toBean(clientHealthLog, ClientHealthLogVO.class);

            // 找到对应的 OrderList
            OrderList orderList = orderLists.stream().filter(ol -> ol.getId().equals(clientHealthLog.getOrderListId())).findFirst().orElse(null);
            if (orderList != null) {
                // 找到对应的 Combo
                combos.stream().filter(c -> c.getComboId().equals(orderList.getComboId())).findFirst().ifPresent(combo -> {
                    String comboImageUrl = comboService.getComboImageUrl(combo.getThumb());
                    bean.setComboImageUrl(comboImageUrl);
                    bean.setComboName(combo.getName());
                });
                bean.setFinishTime(orderList.getFinishTime());
            }
            return bean;
        }).toList();

        // 计算脂肪、蛋白质、碳水化合物、热量
        clientHealthLogVOs = clientHealthLogVOs.stream().map(this::calculateNutritionValues).toList();

        /**
         * 根据订单取餐完成时间分组
         * 早餐：（00：00——10：00）
         * 午餐：（10：00——15：00）
         * 晚餐：（15：00——24：00）
         */
        List<ClientHealthLogListVO> clientHealthLogListVOs = this.groupClientHealthLogsByFinishTime(clientHealthLogVOs);

        return clientHealthLogListVOs;
    }

    private ClientHealthLogVO calculateNutritionValues(ClientHealthLogVO clientHealthLogVO) {
        ClientHealthLogVO bean = BeanUtil.toBean(clientHealthLogVO, ClientHealthLogVO.class);

        if (bean.getStatus() == 0) {
            bean.setFat(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).toString());
            bean.setProtein(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).toString());
            bean.setCarbohydrate(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).toString());
            bean.setKcal(BigDecimal.ZERO.setScale(0, RoundingMode.HALF_UP).intValue());
            bean.setWeight(BigDecimal.ZERO.setScale(1, RoundingMode.HALF_UP).intValue());
            return bean;
        }

        // 等比计算
        BigDecimal div = NumberUtil.div(NumberUtil.toBigDecimal(clientHealthLogVO.getWeight()), 100);

        BigDecimal fat = NumberUtil.mul(div, NumberUtil.toBigDecimal(clientHealthLogVO.getFat()));
        BigDecimal protein = NumberUtil.mul(div, NumberUtil.toBigDecimal(clientHealthLogVO.getProtein()));
        BigDecimal carbohydrate = NumberUtil.mul(div, NumberUtil.toBigDecimal(clientHealthLogVO.getCarbohydrate()));

        // 能量（千焦）= 糖类克数×4 + 蛋白质克数×4 + 脂肪克数×9
        BigDecimal kcal = NumberUtil.add(NumberUtil.mul(carbohydrate, 4), NumberUtil.mul(protein, 4), NumberUtil.mul(fat, 9));
        // 能量（千焦）转为千卡
        kcal = NumberUtil.div(kcal, NumberUtil.toBigDecimal("4.18"));

        bean.setFat(fat.setScale(1, RoundingMode.HALF_UP).toString());
        bean.setProtein(protein.setScale(1, RoundingMode.HALF_UP).toString());
        bean.setCarbohydrate(carbohydrate.setScale(1, RoundingMode.HALF_UP).toString());
        bean.setKcal(kcal.setScale(0, RoundingMode.HALF_UP).intValue());
        bean.setWeight(NumberUtil.toBigDecimal(clientHealthLogVO.getWeight()).setScale(0, RoundingMode.HALF_UP).intValue());
        return bean;
    }

    /**
     * 根据订单取餐完成时间分组
     * 早餐：（00：00——10：00）
     * 午餐：（10：00——15：00）
     * 晚餐：（15：00——24：00）
     *
     * @param clientHealthLogVOs
     * @return
     */
    public List<ClientHealthLogListVO> groupClientHealthLogsByFinishTime(List<ClientHealthLogVO> clientHealthLogVOs) {
        LocalTime breakfastStart = LocalTime.of(0, 0);
        LocalTime breakfastEnd = LocalTime.of(10, 0);
        LocalTime lunchStart = LocalTime.of(10, 0);
        LocalTime lunchEnd = LocalTime.of(15, 0);
        LocalTime dinnerStart = LocalTime.of(15, 0);
        LocalTime dinnerEnd = LocalTime.MIDNIGHT;

        Map<Integer, List<ClientHealthLogVO>> groupedLogs = clientHealthLogVOs.stream().filter(vo -> vo.getFinishTime() != null).collect(Collectors.groupingBy(vo -> {
            LocalTime finishTime = LocalDateTimeUtil.of(vo.getFinishTime()).toLocalTime();
            if (!finishTime.isBefore(breakfastStart) && finishTime.isBefore(breakfastEnd)) {
                return 1;
            } else if (!finishTime.isBefore(lunchStart) && finishTime.isBefore(lunchEnd)) {
                return 2;
            } else if (!finishTime.isBefore(dinnerStart) || finishTime.equals(dinnerEnd)) {
                return 3;
            }
            return 0; // 不属于任何时间段
        }));

        List<ClientHealthLogListVO> clientHealthLogListVOS = new ArrayList<>();
        for (Map.Entry<Integer, List<ClientHealthLogVO>> entry : groupedLogs.entrySet()) {
            if (entry.getKey() != 0 && CollUtil.isNotEmpty(entry.getValue())) {
                ClientHealthLogListVO clientHealthLogListVO = new ClientHealthLogListVO();
                clientHealthLogListVO.setPosition(entry.getKey());
                List<ClientHealthLogVO> collect = entry.getValue().stream().sorted(Comparator.comparing(ClientHealthLogVO::getFinishTime)).collect(Collectors.toList());
                clientHealthLogListVO.setClientHealthLogList(collect);
                clientHealthLogListVOS.add(clientHealthLogListVO);
            }
        }

        return clientHealthLogListVOS.stream().sorted(Comparator.comparing(ClientHealthLogListVO::getPosition)).collect(Collectors.toList());
    }


    @Override
    public ClientHealthLogVO getClientHealthLogById(Long id) {
        ClientHealthLog clientHealthLog = clientHealthLogMapper.selectById(id);
        ClientHealthLogVO bean = BeanUtil.toBean(clientHealthLog, ClientHealthLogVO.class);

        // 填充图片
        OrderList orderList = orderListService.getById(clientHealthLog.getOrderListId());
        Combo combo = comboService.getById(orderList.getComboId());
        String comboImageUrl = comboService.getComboImageUrl(combo.getThumb());
        bean.setComboImageUrl(comboImageUrl);
        bean.setComboName(combo.getName());

        bean.setFinishTime(orderList.getFinishTime());

        // 计算脂肪、蛋白质、碳水化合物、热量
        bean = this.calculateNutritionValues(bean);
        return bean;
    }

    @Override
    public void updateClientHealthLogStatus(ClientHealthLogDTO clientHealthLogDTO) {
        clientHealthLogMapper.updateStatusById(clientHealthLogDTO.getId(), clientHealthLogDTO.getStatus());
    }

    @Override
    public void updateClientHealthLogWeight(ClientHealthLogDTO clientHealthLogDTO) {
        clientHealthLogMapper.updateWeightById(clientHealthLogDTO.getId(), clientHealthLogDTO.getWeight());
    }

    @Override
    public ClientHealthLogVO estimateWeightByGram(ClientHealthLogDTO clientHealthLogDTO) {
        ClientHealthLog clientHealthLog = clientHealthLogMapper.selectById(clientHealthLogDTO.getId());
        ClientHealthLogVO bean = BeanUtil.toBean(clientHealthLog, ClientHealthLogVO.class);

        bean.setWeight(clientHealthLogDTO.getWeight());
        // 计算脂肪、蛋白质、碳水化合物、热量
        bean = this.calculateNutritionValues(bean);

        return bean;
    }
}




